<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>数字人项目流程图</title>
    <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: 'Microsoft YaHei', 'Segoe UI', Arial, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            padding: 20px;
            min-height: 100vh;
        }
        
        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: white;
            border-radius: 20px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            padding: 40px;
        }
        
        h1 {
            text-align: center;
            color: #333;
            margin-bottom: 10px;
            font-size: 2.5em;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }
        
        .subtitle {
            text-align: center;
            color: #666;
            margin-bottom: 40px;
            font-size: 1.1em;
        }
        
        .section {
            margin-bottom: 60px;
            padding: 30px;
            background: #f8f9fa;
            border-radius: 15px;
            border-left: 5px solid #667eea;
        }
        
        .section h2 {
            color: #333;
            margin-bottom: 20px;
            font-size: 1.8em;
            padding-bottom: 10px;
            border-bottom: 2px solid #667eea;
        }
        
        .section p {
            color: #555;
            line-height: 1.8;
            margin-bottom: 20px;
            font-size: 1.1em;
        }
        
        .mermaid {
            background: white;
            border-radius: 10px;
            padding: 20px;
            margin: 20px 0;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }
        
        .tech-stack {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 20px;
            margin-top: 30px;
        }
        
        .tech-card {
            background: white;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            border-top: 3px solid #667eea;
        }
        
        .tech-card h3 {
            color: #667eea;
            margin-bottom: 10px;
            font-size: 1.3em;
        }
        
        .tech-card ul {
            list-style: none;
            padding-left: 0;
        }
        
        .tech-card li {
            padding: 8px 0;
            color: #555;
            border-bottom: 1px solid #eee;
        }
        
        .tech-card li:last-child {
            border-bottom: none;
        }
        
        .tech-card li:before {
            content: "▸ ";
            color: #667eea;
            font-weight: bold;
            margin-right: 8px;
        }
        
        .comparison-table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
            background: white;
            border-radius: 10px;
            overflow: hidden;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        
        .comparison-table th {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 15px;
            text-align: left;
            font-weight: 600;
        }
        
        .comparison-table td {
            padding: 15px;
            border-bottom: 1px solid #eee;
            color: #555;
        }
        
        .comparison-table tr:hover {
            background: #f8f9fa;
        }
        
        .comparison-table tr:last-child td {
            border-bottom: none;
        }
        
        .highlight {
            background: #fff3cd;
            padding: 2px 6px;
            border-radius: 4px;
            font-weight: 600;
        }
        
        .footer {
            text-align: center;
            margin-top: 40px;
            padding-top: 20px;
            border-top: 2px solid #eee;
            color: #999;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>数字人项目技术流程图</h1>
        <p class="subtitle">Num页面 - AI对话语音识别完整技术架构</p>
        
        <!-- 项目整体架构 -->
        <div class="section">
            <h2>📋 项目整体架构</h2>
            <div class="mermaid">
                graph TB
                    A[用户访问Num页面] --> B[页面加载]
                    B --> C[初始化3D模型]
                    B --> D[初始化视频播放器]
                    B --> E[初始化AI对话面板]
                    
                    E --> F[方案一: 浏览器原生API]
                    E --> G[方案二: 科大讯飞ASR]
                    
                    F --> H[底部💬按钮]
                    G --> I[AI对话面板🎤按钮]
                    
                    H --> J[语音识别结果]
                    I --> J
                    
                    J --> K[更新输入框]
                    K --> L[发送AI对话请求]
                    L --> M[Moonshot API]
                    M --> N[返回AI回复]
                    N --> O[科大讯飞TTS]
                    O --> P[语音播报]
                    
                    style A fill:#667eea,stroke:#764ba2,color:#fff
                    style J fill:#48bb78,stroke:#38a169,color:#fff
                    style M fill:#ed8936,stroke:#dd6b20,color:#fff
                    style P fill:#9f7aea,stroke:#805ad5,color:#fff
            </div>
        </div>
        
        <!-- 方案一：浏览器原生API流程 -->
        <div class="section">
            <h2>🎙️ 方案一：浏览器原生 Web Speech API 流程</h2>
            <div class="mermaid">
                sequenceDiagram
                    participant U as 用户
                    participant B as 浏览器
                    participant API as Web Speech API
                    participant C as 云端服务
                    
                    U->>B: 点击💬按钮
                    B->>B: 检查浏览器支持
                    B->>B: 请求麦克风权限
                    B->>U: 显示权限请求
                    U->>B: 授权麦克风
                    B->>API: 创建SpeechRecognition实例
                    B->>API: 配置参数(连续识别,中文,中间结果)
                    B->>API: 启动识别
                    API->>C: 实时上传音频流
                    C->>API: 返回识别结果
                    API->>B: onresult事件(中间结果)
                    B->>U: 实时显示识别文本
                    API->>B: onresult事件(最终结果)
                    B->>U: 更新最终文本
                    U->>B: 点击停止/自动停止
                    B->>API: 停止识别
                    API->>B: onend事件
                    B->>B: 处理最终结果
                    B->>U: 更新输入框
            </div>
            
            <div class="tech-stack">
                <div class="tech-card">
                    <h3>🔧 核心技术</h3>
                    <ul>
                        <li>Web Speech API</li>
                        <li>SpeechRecognition</li>
                        <li>getUserMedia API</li>
                        <li>事件驱动机制</li>
                    </ul>
                </div>
                <div class="tech-card">
                    <h3>⚙️ 配置参数</h3>
                    <ul>
                        <li>continuous: true</li>
                        <li>interimResults: true</li>
                        <li>lang: 'zh-CN'</li>
                        <li>自动静音检测</li>
                    </ul>
                </div>
                <div class="tech-card">
                    <h3>📊 特点</h3>
                    <ul>
                        <li>零配置启动</li>
                        <li>实时中间结果</li>
                        <li>浏览器内置支持</li>
                        <li>需要HTTPS环境</li>
                    </ul>
                </div>
            </div>
        </div>
        
        <!-- 方案二：科大讯飞ASR流程 -->
        <div class="section">
            <h2>🎤 方案二：科大讯飞 ASR 完整流程</h2>
            <div class="mermaid">
                graph TD
                    A[用户点击🎤按钮] --> B[创建XfAsrRecognition实例]
                    B --> C[请求麦克风权限]
                    C --> D{用户授权?}
                    D -->|否| E[显示权限错误]
                    D -->|是| F[获取MediaStream]
                    F --> G[创建AudioContext<br/>16kHz采样率]
                    G --> H[创建音频处理节点<br/>ScriptProcessorNode]
                    H --> I[生成签名URL<br/>HMAC-SHA256]
                    I --> J[建立WebSocket连接<br/>wss://iat-api.xfyun.cn]
                    J --> K{连接成功?}
                    K -->|否| L[显示网络错误]
                    K -->|是| M[发送握手帧<br/>status: 0]
                    M --> N[启动音频处理循环]
                    N --> O[每0.25秒采集音频]
                    O --> P[Float32 → Int16 PCM转换]
                    P --> Q[Base64编码]
                    Q --> R[发送音频数据帧<br/>status: 1]
                    R --> S[服务器实时识别]
                    S --> T{返回结果类型?}
                    T -->|中间结果| U[实时显示临时文本]
                    T -->|最终结果| V[更新累积文本]
                    U --> W[继续采集]
                    V --> W
                    W --> X{用户停止?}
                    X -->|否| O
                    X -->|是| Y[发送结束帧<br/>status: 2]
                    Y --> Z[清理音频资源]
                    Z --> AA[关闭WebSocket]
                    AA --> AB[更新UI]
                    
                    style A fill:#667eea,stroke:#764ba2,color:#fff
                    style J fill:#48bb78,stroke:#38a169,color:#fff
                    style S fill:#ed8936,stroke:#dd6b20,color:#fff
                    style AB fill:#9f7aea,stroke:#805ad5,color:#fff
                    style E fill:#f56565,stroke:#e53e3e,color:#fff
                    style L fill:#f56565,stroke:#e53e3e,color:#fff
            </div>
            
            <div class="mermaid">
                sequenceDiagram
                    participant U as 用户
                    participant P as 页面组件
                    participant ASR as XfAsrRecognition
                    participant AC as AudioContext
                    participant WS as WebSocket
                    participant XF as 科大讯飞服务器
                    
                    U->>P: 点击🎤按钮
                    P->>ASR: 创建实例(配置回调)
                    ASR->>ASR: 请求麦克风权限
                    ASR->>U: 显示权限请求
                    U->>ASR: 授权
                    ASR->>AC: 创建AudioContext(16kHz)
                    ASR->>AC: 创建音频处理节点
                    ASR->>ASR: 生成签名URL(HMAC-SHA256)
                    ASR->>WS: 建立连接
                    WS->>XF: WebSocket握手
                    XF->>WS: 握手响应
                    WS->>ASR: onopen事件
                    ASR->>XF: 发送握手帧(status:0)
                    ASR->>ASR: 启动音频处理循环
                    
                    loop 每0.25秒
                        AC->>ASR: 音频数据(Float32)
                        ASR->>ASR: 转换为Int16 PCM
                        ASR->>ASR: Base64编码
                        ASR->>XF: 发送音频帧(status:1)
                    end
                    
                    XF->>WS: 返回识别结果(中间)
                    WS->>ASR: onmessage(中间结果)
                    ASR->>P: onResult回调(false)
                    P->>U: 实时显示临时文本
                    
                    XF->>WS: 返回识别结果(最终)
                    WS->>ASR: onmessage(最终结果)
                    ASR->>ASR: 累积文本
                    ASR->>P: onResult回调(true)
                    P->>U: 更新最终文本
                    
                    U->>P: 点击停止
                    P->>ASR: stop()
                    ASR->>XF: 发送结束帧(status:2)
                    ASR->>AC: 清理音频资源
                    ASR->>WS: 关闭连接
                    ASR->>P: onEnd回调
                    P->>U: 更新UI状态
            </div>
        </div>
        
        <!-- 音频处理详细流程 -->
        <div class="section">
            <h2>🔊 音频处理技术流程</h2>
            <div class="mermaid">
                graph LR
                    A[麦克风硬件] --> B[操作系统音频驱动]
                    B --> C[浏览器MediaStream API]
                    C --> D[getUserMedia获取流]
                    D --> E[AudioContext.createMediaStreamSource]
                    E --> F[ScriptProcessorNode<br/>缓冲区4096样本]
                    F --> G[Float32Array<br/>范围-1.0到1.0]
                    G --> H[格式转换函数]
                    H --> I[Int16Array<br/>范围-32768到32767]
                    I --> J[Base64编码]
                    J --> K[WebSocket发送]
                    K --> L[科大讯飞服务器]
                    
                    style A fill:#667eea,stroke:#764ba2,color:#fff
                    style F fill:#48bb78,stroke:#38a169,color:#fff
                    style I fill:#ed8936,stroke:#dd6b20,color:#fff
                    style L fill:#9f7aea,stroke:#805ad5,color:#fff
            </div>
            
            <div class="mermaid">
                graph TD
                    A[音频数据帧时序] --> B[0ms: 发送握手帧]
                    B --> C[~250ms: 第一帧音频]
                    C --> D[~500ms: 第二帧音频]
                    D --> E[~750ms: 第三帧音频]
                    E --> F[持续发送...]
                    F --> G[用户停止: 结束帧]
                    
                    H[帧类型] --> I[status: 0<br/>握手帧/第一帧]
                    H --> J[status: 1<br/>数据帧]
                    H --> K[status: 2<br/>结束帧]
                    
                    style B fill:#667eea,stroke:#764ba2,color:#fff
                    style G fill:#f56565,stroke:#e53e3e,color:#fff
                    style I fill:#48bb78,stroke:#38a169,color:#fff
                    style J fill:#ed8936,stroke:#dd6b20,color:#fff
                    style K fill:#9f7aea,stroke:#805ad5,color:#fff
            </div>
        </div>
        
        <!-- 结果处理流程 -->
        <div class="section">
            <h2>📝 识别结果处理流程</h2>
            <div class="mermaid">
                graph TD
                    A[接收服务器响应] --> B[解析JSON数据]
                    B --> C{检查错误码}
                    C -->|错误| D[触发onError回调]
                    C -->|成功| E[提取payload.result]
                    E --> F{结果类型判断}
                    F -->|中间结果| G[pgs非rpl/apd]
                    F -->|最终结果| H[pgs=rpl或apd]
                    G --> I[显示: 累积文本+临时文本]
                    H --> J{处理模式?}
                    J -->|rpl替换| K[替换之前文本]
                    J -->|apd追加| L[追加到当前文本]
                    K --> M[更新currentText]
                    L --> M
                    M --> N[触发onResult回调<br/>isFinal: true]
                    I --> O[触发onResult回调<br/>isFinal: false]
                    N --> P[更新UI输入框]
                    O --> P
                    P --> Q{最终结果?}
                    Q -->|是| R[自动打开聊天面板]
                    Q -->|否| S[继续显示]
                    
                    style A fill:#667eea,stroke:#764ba2,color:#fff
                    style D fill:#f56565,stroke:#e53e3e,color:#fff
                    style M fill:#48bb78,stroke:#38a169,color:#fff
                    style R fill:#9f7aea,stroke:#805ad5,color:#fff
            </div>
        </div>
        
        <!-- 技术对比 -->
        <div class="section">
            <h2>⚖️ 两种方案技术对比</h2>
            <table class="comparison-table">
                <thead>
                    <tr>
                        <th>技术维度</th>
                        <th>浏览器原生 API</th>
                        <th>科大讯飞 ASR</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td><strong>API 类型</strong></td>
                        <td>Web Speech API</td>
                        <td>WebSocket + Web Audio API</td>
                    </tr>
                    <tr>
                        <td><strong>音频采集</strong></td>
                        <td>浏览器自动处理</td>
                        <td>手动使用 getUserMedia + AudioContext</td>
                    </tr>
                    <tr>
                        <td><strong>音频处理</strong></td>
                        <td>浏览器内部处理</td>
                        <td>手动转换 Float32 → Int16 PCM → Base64</td>
                    </tr>
                    <tr>
                        <td><strong>网络协议</strong></td>
                        <td>HTTP/HTTPS（浏览器内部）</td>
                        <td>WebSocket（WSS）</td>
                    </tr>
                    <tr>
                        <td><strong>认证方式</strong></td>
                        <td>浏览器自动处理</td>
                        <td>HMAC-SHA256 签名认证</td>
                    </tr>
                    <tr>
                        <td><strong>数据格式</strong></td>
                        <td>浏览器内部格式</td>
                        <td>PCM 16bit, 16kHz, Base64 编码</td>
                    </tr>
                    <tr>
                        <td><strong>实时性</strong></td>
                        <td>实时（浏览器优化）</td>
                        <td>实时（手动控制）</td>
                    </tr>
                    <tr>
                        <td><strong>控制粒度</strong></td>
                        <td>较低（浏览器限制）</td>
                        <td>较高（完全自定义）</td>
                    </tr>
                    <tr>
                        <td><strong>识别准确度</strong></td>
                        <td>中等</td>
                        <td>较高（专业服务）</td>
                    </tr>
                    <tr>
                        <td><strong>成本</strong></td>
                        <td>免费</td>
                        <td>需要付费 API</td>
                    </tr>
                    <tr>
                        <td><strong>配置复杂度</strong></td>
                        <td>简单</td>
                        <td>复杂</td>
                    </tr>
                    <tr>
                        <td><strong>适用场景</strong></td>
                        <td>快速测试、简单场景</td>
                        <td>生产环境、高准确度需求</td>
                    </tr>
                </tbody>
            </table>
        </div>
        
        <!-- 技术栈 -->
        <div class="section">
            <h2>🛠️ 项目技术栈</h2>
            <div class="tech-stack">
                <div class="tech-card">
                    <h3>前端框架</h3>
                    <ul>
                        <li>React 18</li>
                        <li>TypeScript</li>
                        <li>React Router</li>
                        <li>Vite</li>
                    </ul>
                </div>
                <div class="tech-card">
                    <h3>3D 渲染</h3>
                    <ul>
                        <li>React Three Fiber</li>
                        <li>Three.js</li>
                        <li>GLTFLoader</li>
                        <li>OrbitControls</li>
                    </ul>
                </div>
                <div class="tech-card">
                    <h3>语音识别</h3>
                    <ul>
                        <li>Web Speech API</li>
                        <li>Web Audio API</li>
                        <li>WebSocket</li>
                        <li>科大讯飞 ASR</li>
                    </ul>
                </div>
                <div class="tech-card">
                    <h3>语音合成</h3>
                    <ul>
                        <li>科大讯飞 TTS</li>
                        <li>MediaSource API</li>
                        <li>MP3 流式播放</li>
                    </ul>
                </div>
                <div class="tech-card">
                    <h3>AI 对话</h3>
                    <ul>
                        <li>Moonshot API</li>
                        <li>流式响应处理</li>
                        <li>SSE 格式解析</li>
                    </ul>
                </div>
                <div class="tech-card">
                    <h3>其他功能</h3>
                    <ul>
                        <li>视频播放控制</li>
                        <li>弹幕系统</li>
                        <li>文件上传</li>
                        <li>画中画模式</li>
                    </ul>
                </div>
            </div>
        </div>
        
        <div class="footer">
            <p>数字人项目技术流程图 | 生成时间: <span id="currentDate"></span></p>
        </div>
    </div>
    
    <script>
        // 初始化 Mermaid
        mermaid.initialize({ 
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            },
            sequence: {
                diagramMarginX: 50,
                diagramMarginY: 10,
                actorMargin: 50,
                width: 150,
                height: 65,
                boxMargin: 10,
                boxTextMargin: 5,
                noteMargin: 10,
                messageMargin: 35
            }
        });
        
        // 设置当前日期
        document.getElementById('currentDate').textContent = new Date().toLocaleString('zh-CN');
    </script>
</body>
</html>
